<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    
</body>
</html>
<script>
    // Generator.prototype.return()

    // Generator函数返回的遍历器对象 还有一个return方法 可以返回给定的值 并且终结遍历Generator函数
    function* gen() {
        yield 1;
        yield 2;
        yield 3;
    }

    var g = gen();

    g.next()        // { value: 1, done: false }
    g.return('foo') // { value: "foo", done: true }
    g.next()        // { value: undefined, done: true }

    // 上面代码中 遍历器对象g调用return方法后 返回值的value属性就是return方法的参数foo
    // 并且 Generator 函数的遍历就终止了 返回值的done属性为true 
    // 以后再调用next方法 done属性总是返回true


    // 如果return方法调用时 不提供参数 则返回值的value属性为undefined
    function* gen() {
        yield 1;
        yield 2;
        yield 3;
    }

    var g = gen();

    g.next()       // { value: 1, done: false }
    g.return()     // { value: undefined, done: true }


    // 如果Generator内部有try...finally代码块 且正在执行try代码块
    // 那么return方法会推迟到finally代码块执行完再执行
    function* numbers () {
        yield 1;
        try {
            yield 2;
            yield 3;
        } finally {
            yield 4;
            yield 5;
        }
        yield 6;
    }
    var g = numbers();
    g.next() // { value: 1, done: false }
    g.next() // { value: 2, done: false }
    g.return(7) // { value: 4, done: false }
    g.next() // { value: 5, done: false }
    g.next() // { value: 7, done: true }


    // 上面代码中 调用return方法后 就开始执行finally代码块
    // 然后等到finally代码块执行完 再执行return方法


    // next() throw() return() 共同点
    // next()是将yield表达式替换成一个值
    const g = function* (x, y) {
        let result = yield x + y;
        return result;
    };

    const gen = g(1, 2);
    gen.next(); // Object {value: 3, done: false}

    gen.next(1); // Object {value: 1, done: true}
    // 相当于将 let result = yield x + y
    // 替换成 let result = 1;
    // 上面代码中 第二个next(1)方法就相当于将yield表达式替换成一个值1
    // 如果next方法没有参数 就相当于替换成undefined




    // throw()是将yield表达式替换成一个throw语句
    gen.throw(new Error('出错了')); // Uncaught Error: 出错了
    // 相当于将 let result = yield x + y
    // 替换成 let result = throw(new Error('出错了'));


    // return()是将yield表达式替换成一个return语句
    gen.return(2); // Object {value: 2, done: true}
    // 相当于将 let result = yield x + y
    // 替换成 let result = return 2;


    // yield* 表达式
    // 如果在 Generator 函数内部 调用另一个 Generator 函数 需要在前者的函数体内部 自己手动完成遍历
    function* foo() {
        yield 'a';
        yield 'b';
    }

    function* bar() {
        yield 'x';
        // 手动遍历 foo()
        for (let i of foo()) {
            console.log(i);
        }
        yield 'y';
    }

    for (let v of bar()){
        console.log(v);
    }
    // x
    // a
    // b
    // y
    // 上面代码中 foo和bar都是 Generator 函数 在bar里面调用foo 就需要手动遍历foo
    // 如果有多个 Generator 函数嵌套 写起来就非常麻烦


    // ES6提供了yield* 表达式 作为解决办法 用来在一个Generator函数里面执行另一个Generator函数
    function* bar() {
        yield 'x';
        yield* foo();
        yield 'y';
    }

    // 等同于
    function* bar() {
        yield 'x';
        yield 'a';
        yield 'b';
        yield 'y';
    }

    // 等同于
    function* bar() {
        yield 'x';
        for (let v of foo()) {
            yield v;
        }
        yield 'y';
    }

    for (let v of bar()){
        console.log(v);
    }
    // "x"
    // "a"
    // "b"
    // "y"


    



</script>